package com.syg.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.util.Assert;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author ps
 */
@Slf4j
public class BeanUtil extends BeanUtils {

    /**
     * 不需要目标对象实例
     * @param source
     * @param target
     * @param <T>
     * @return
     * @throws BeansException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static <T> T copyProperties(Object source, Class <T> target) {
        T o = null;
        try {
            o = target.newInstance();
        } catch (InstantiationException e) {
            log.error("BeanUtils.copyProperties.01", e);
        } catch (IllegalAccessException e) {
            log.error("BeanUtils.copyProperties.02", e);
        }
        // 这个方法命名抛出异常了,为什么我不用处理？
        assert o != null;
        BeanUtils.copyProperties(source, o);
        return o;
    }

    /**
     * 不需要目标对象实例
     * @param source
     * @param target
     * @param ignoreProperties
     * @param <T>
     * @return
     * @throws BeansException
     */
    public static <T> T copyProperties(Object source, Class <T> target, String... ignoreProperties) throws BeansException {
        T o = null;
        try {
            o = target.newInstance();
        } catch (InstantiationException e) {
            log.error("BeanUtils.copyProperties.01", e);
        } catch (IllegalAccessException e) {
            log.error("BeanUtils.copyProperties.02", e);
        }
        // 这个方法命名抛出异常了,为什么我不用处理？
        assert o != null;
        BeanUtils.copyProperties(source, o, ignoreProperties);
        return o;
    }

    /**
     * 不需要目标对象实例
     * @param sourceList
     * @param target
     * @param <E>
     * @param <T>
     * @return
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public static <E,T> List<T> copyList(List<E> sourceList,Class<T> target) {
        if (null == sourceList) {
            return null;
        }
        if (sourceList.isEmpty()) {
            return new ArrayList<>();
        }

        List<T> resultList = new ArrayList<>();
        for (E item : sourceList) {
            T t = copyProperties(item, target);
            resultList.add(t);
        }
        return resultList;
    }

    /**
     * 不需要目标对象实例
     *
     * @param sourceList
     * @param target
     * @param <E>
     * @param <T>
     * @return
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public static <E, T> List<T> copyList(List<E> sourceList, Class<T> target, String... ignoreProperties) {
        if (null == sourceList) {
            return null;
        }
        if (sourceList.isEmpty()) {
            return new ArrayList<>();
        }

        List<T> resultList = new ArrayList<>();
        for (E item : sourceList) {
            T t = copyProperties(item, target, ignoreProperties);
            resultList.add(t);
        }
        return resultList;
    }

    /**
     *
     * sourceObj补充赋值destObj，destObj中有值属性不会再次被赋值
     *
     * @param target
     * @param sourceObj
     */
    public static void copyPropertiesExclude(Object target, Object sourceObj) {
        Assert.notNull(target, "target must not be null");
        Assert.notNull(sourceObj, "sourceObj must not be null");

        String[] ignoreProperties = getNotNullProperties(target);
        BeanUtils.copyProperties(sourceObj,target, ignoreProperties);
    }

    /**
     *
     * sourceObj覆盖destObj，sourceObj有值属性会赋值destObj中的属性，无值属性不会去覆盖
     *
     * @param destObj
     * @param sourceObj
     */
    public static void copyPropertiesExcludeEmpty(Object destObj, Object sourceObj) {
        Assert.notNull(destObj, "destObj must not be null");
        Assert.notNull(sourceObj, "sourceObj must not be null");
        String[] ignoreProperties = getNullProperties(sourceObj);
        BeanUtils.copyProperties(destObj, sourceObj, ignoreProperties);
    }


    /**
     * sourceObj覆盖destObj，sourceObj有值属性会赋值destObj中的无值属性
     *
     * @param destObj
     * @param sourceObj
     */
    public static void copyPropertiesExcludeSrcEmptyAndDestNotEmpty(Object destObj, Object sourceObj) {
        Set<String> resultList=new HashSet<>();
        String[] excludeStr = new String[] {};
        resultList.addAll(Arrays.asList(getNotNullProperties(destObj)));
        resultList.addAll(Arrays.asList(getNullProperties(sourceObj)));
        excludeStr = resultList.toArray(excludeStr);
        BeanUtils.copyProperties(destObj, sourceObj, excludeStr);
    }

    /**
     *
     * 获取实例对象的非空属性
     *
     * @param obj
     * @return
     */
    @SuppressWarnings("rawtypes")
    private static String[] getNotNullProperties(Object obj) {
        Assert.notNull(obj, "obj must not be null");

        List<String> list = new ArrayList<>();
        list.add("id");
        list.add("serialVersionUID");
        //  得到类对象
        Class clazz = obj.getClass();
        while (clazz != null && clazz != Object.class) {
            // 得到属性集合
            Field[] fields = clazz.getDeclaredFields();
            // 遍历属性
            for (Field f : fields) {
                if (!f.isAccessible()) {
                    //  设置属性访问权限（可以访问私有属性）
                    f.setAccessible(true);
                }
                try {
                    Object value = f.get(obj);
                    if (value != null) {
                        list.add(f.getName());
                    }
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    log.error("处理异常:",e);
                }
            }
            //  得到父类对象
            clazz = clazz.getSuperclass();
        }
        String[]  notNullProperties = new String[list.size()];
        list.toArray(notNullProperties);
        return notNullProperties;
    }

    /**
     *
     * 获取实例对象的空属性
     *
     * @author supk
     * @date 2019年9月29日 上午10:51:36
     * @param obj
     * @return
     */
    @SuppressWarnings("rawtypes")
    private static String[] getNullProperties(Object obj) {
        Assert.notNull(obj, "obj must not be null");
        List<String> list = new ArrayList<>();
        list.add("id");
        list.add("serialVersionUID");
        //  得到类对象
        Class clazz = obj.getClass();
        while (clazz != null && clazz != Object.class) {
            // 得到属性集合
            Field[] fields = clazz.getDeclaredFields();
            // 遍历属性
            for (Field f : fields) {
                if (!f.isAccessible()) {
                    //  设置属性访问权限（可以访问私有属性）
                    f.setAccessible(true);
                }
                try {
                    Object value = f.get(obj);
                    if (value == null) {
                        list.add(f.getName());
                    }
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    log.error("处理异常:",e);
                }
            }
            //  得到父类对象
            clazz = clazz.getSuperclass();
        }
        String[] nullProperties = new String[list.size()];
        list.toArray(nullProperties);
        return nullProperties;
    }
}
